package com.pkk.components.dynamicdb.aspect;

import com.pkk.components.dynamicdb.annotation.ActivateDataSource;
import com.pkk.components.dynamicdb.constants.DataSourceContants;
import com.pkk.components.dynamicdb.util.DataSourceUtil;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

@Aspect
@Order(-88)// AOP在@Transactional之前执行，否则会事物执行失败
@Slf4j
@Component
public class DataSourceAspect {

  @Before("@annotation(ads)")
  public void activateDataSource(JoinPoint point, ActivateDataSource ads) throws Throwable {
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    String keyDataSource = this.getDataSourceName(point.getArgs(), method, ads);
    if (!process(keyDataSource, point)) {
      return;
    }
    log.debug("method：{} ", point.getSignature().getName());
    DataSourceUtil.activateDataSource(keyDataSource, null);
  }

  private String getDataSourceName(Object[] args, Method method, ActivateDataSource ds) {
    if (StringUtils.isEmpty(ds.value()) && StringUtils.isEmpty(ds.spel())) {
      log.debug("datasource name or spel is null ");
      return null;
    }
    if (!StringUtils.isEmpty(ds.value())) {
      return ds.value();
    }
    // 获取被拦截方法参数名列表(使用Spring支持类库)
    LocalVariableTableParameterNameDiscoverer nameDiscoverer =
        new LocalVariableTableParameterNameDiscoverer();
    String[] parameterNames = nameDiscoverer.getParameterNames(method);
    String name = this.parseKey(args, parameterNames, ds.spel());
    if (StringUtils.isEmpty(name)) {
      log.debug("datasource name or spel is null ");
      return null;
    }
    return name;
  }

  /**
   * @param args 参数值
   * @param parameterNames 参数名
   * @param complexKey key值
   * @return
   */
  private String parseKey(Object[] args, String[] parameterNames, String complexKey) {
    // 把方法参数名与参数值成对放入SPEL上下文中
    EvaluationContext expressionContext = new StandardEvaluationContext();
    for (int i = 0; i < parameterNames.length; i++) {
      expressionContext.setVariable(parameterNames[i], args[i]);
    }
    // 解析key
    ExpressionParser parser = new SpelExpressionParser();
    return parser.parseExpression(complexKey).getValue(expressionContext, String.class);
  }

  @After("@annotation(ads)")
  public void resetDataSource(JoinPoint point, ActivateDataSource ads) {
    String keyDataSource = ads.value();
    if (!process(keyDataSource, point)) {
      return;
    }
    log.debug("method：{} ", point.getSignature().getName());
    DataSourceUtil.resetDataSource(keyDataSource, ads.tmp());
  }

  private boolean process(String keyDataSource, JoinPoint point) {
    if (keyDataSource == null) {
      log.debug("数据源注解已经标识，但value为null[{}]", point.getSignature().getName());
      return false;
    }
    if (keyDataSource.equals(DataSourceContants.MAINDATASOURCE)) {
      return false;
    }

    return true;
  }
}